/* process.cc
	A class for holding process/thread related information in NACHOS
 */

#include "process.h"
#include "synch.h"
#include "thread.h"
#include "addrspace.h"

Process::Process(char* debugName) {
	processID = -1;
	name = debugName;
	space = NULL;
	threadCount = 0;
	activeThreadCount = 0;
	waitThreadCount = 0;
	signalThreadCount = 0;
	threadsMap = new BitMap(MAX_THREADS);
	threadsMapLock = new Lock("threadsMapLock");
}

Process::Process(char* debugName, int otherProcessID, AddrSpace* addrSpace) {
	processID = otherProcessID;
	name = debugName;
	space = addrSpace;
	threadCount = 0;
	activeThreadCount = 0;
	waitThreadCount = 0;
	signalThreadCount = 0;
	threadsMap = new BitMap(MAX_THREADS);
	threadsMapLock = new Lock("threadsMapLock");
}

Process::~Process() {
	delete[] name;
	delete[] threadsMap;
	delete threadsMapLock;
}

void
Process::setProcessID(int otherProcessID) {
	processID = otherProcessID;
}

int
Process::getProcessID()  {
	return processID;
}

void
Process::setAddrSpace(AddrSpace* addrSpace) {
	space = addrSpace;
}

AddrSpace*
Process::getAddrSpace() {
	return space;
}

Thread*
Process::createThread(char* debugName) {
	int threadID = -1;
	Thread* thread = NULL;

	//threadsMapLock->Acquire();
	threadID = threadsMap->Find();

	if (threadID == -1) {
		DEBUG('p', "[ERROR][createThread] Cannot allocate more Thread to Process [%d].\n", processID);
		//printf("[ERROR][createThread] Cannot allocate more Thread to Process [%d].\n", processID);
		//interrupt->Halt();
		//return;
	}
	else {
		DEBUG('p', "[INFO][createThread] Thread [%d] created for Process [%d].\n", threadID, processID);
		//printf("[INFO][createThread] Thread [%d] created for Process [%d].\n", threadID, processID);
		thread = new Thread(debugName);
		threads[threadID] = thread;
		thread->process = this;
		thread->space = space;
		thread->threadID = threadID;
		threadCount++;
		activeThreadCount++;
	}

	//threadsMapLock->Release();
	
	return thread;
}

int
Process::addThread(Thread* thread) {
	int threadID = -1;

	if (thread->space == NULL) {
		DEBUG('p', "[ERROR][addThread] Thread being added to Process [%d] hasn't been allocated with AddrSpace.\n", processID);
		//printf("[ERROR][addThread] Thread being added to Process [%d] hasn't been allocated with AddrSpace.\n", processID);
		//interrupt->Halt();
		//return;
		return threadID;
	}

	if (thread->space != space) {
		DEBUG('p', "[ERROR][addThread] Thread being added to Process [%d] doesn't have the same AddrSpace.\n", processID);
		//printf("[ERROR][addThread] Thread being added to Process [%d] doesn't have the same AddrSpace.\n", processID);
		//interrupt->Halt();
		//return;
		return threadID;
	}

	//threadsMapLock->Acquire();
	threadID = threadsMap->Find();

	if (threadID == -1) {
		DEBUG('p', "[ERROR][addThread] Cannot allocate more Thread to Process [%d].\n", processID);
		//printf("[ERROR][addThread] Cannot allocate more Thread to Process [%d].\n", processID);
		//interrupt->Halt();
		//return;
	}
	else {
		DEBUG('p', "[INFO][addThread] Thread [%d] created for Process [%d].\n", threadID, processID);
		//printf("[INFO][addThread] Thread [%d] created for Process [%d].\n", threadID, processID);
		threads[threadID] = thread;
		thread->process = this;
		thread->threadID = threadID;
		threadCount++;
		activeThreadCount++;
	}

	//threadsMapLock->Release();
	
	return threadID;
}

void
Process::removeThread(int threadID) {
	if (threadID < 0 || threadID >= MAX_THREADS) {
		DEBUG('p', "[ERROR][removeThread] Thread [%d] is invalid for removal for Process [%d].\n", threadID, processID);
		//printf("[ERROR][removeThread] Thread [%d] is invalid for removal for Process [%d].\n", threadID, processID);
		//interrupt->Halt();
		return;
	}

	//threadsMapLock->Acquire();
	
	if (!threadsMap->Test(threadID)) {
		DEBUG('p', "[ERROR][removeThread] Thread [%d] was not allocated for Process [%d].\n", threadID, processID);
		//printf("[ERROR][removeThread] Thread [%d] was not allocated for Process [%d].\n", threadID, processID);
		//interrupt->Halt();
		//return;
	}
	else {
		DEBUG('p', "[INFO][removeThread] Thread [%d] removed for Process [%d].\n", threadID, processID);
		//printf("[INFO][removeThread] Thread [%d] removed for Process [%d].\n", threadID, processID);
		threads[threadID] = NULL;
		threadsMap->Clear(threadID);
		threadCount--;
		activeThreadCount--;
	}

	//threadsMapLock->Release();
}

int
Process::getThreadCount() {
	//threadsMapLock->Acquire();
	if (threadsMap->NumClear() != (MAX_THREADS-threadCount)) {
		printf("[ERROR][getThreadCount] Number of Threads isn't maintained properly for Process [%d].\n", processID);
		//interrupt->Halt();
		//return;
	}
	//threadsMapLock->Release();

	return threadCount;	
}

Thread*
Process::getThread(int threadID) {
	Thread* thread = NULL;

	if (threadID < 0 || threadID >= MAX_THREADS) {
		DEBUG('p', "[ERROR][getThread] Thread [%d] is invalid for retrival for Process [%d].\n", threadID, processID);
		//printf("[ERROR][getThread] Thread [%d] is invalid for retrival for Process [%d].\n", threadID, processID);
		//interrupt->Halt();
		//return;
		return NULL;
	}

	//threadsMapLock->Acquire();
	
	if (!threadsMap->Test(threadID)) {
		DEBUG('p', "[ERROR][getThread] Thread [%d] was not allocated for Process [%d].\n", threadID, processID);
		//printf("[ERROR][getThread] Thread [%d] was not allocated for Process [%d].\n", threadID, processID);
		//interrupt->Halt();
		//return;
	}
	else {
		DEBUG('p', "[INFO][getThread] Thread [%d] retrived for Process [%d].\n", threadID, processID);
		//printf("[INFO][getThread] Thread [%d] retrived for Process [%d].\n", threadID, processID);
		thread = threads[threadID];
	}

	//threadsMapLock->Release();

	return thread;
}

int
Process::getActiveThreadCount() {
	return activeThreadCount;
}

void
Process::increaseActiveThreadCount() {
	//threadsMapLock->Acquire();
	//if (activeThreadCount < threadCount) {
		activeThreadCount++;
	//}
	//threadsMapLock->Release();
}

void
Process::decreaseActiveThreadCount() {
	//threadsMapLock->Acquire();
	//if (activeThreadCount > 0) {
		activeThreadCount--;
	//}
	//threadsMapLock->Release();
}

int
Process::getWaitThreadCount() {
	return waitThreadCount;
}

void
Process::increaseWaitThreadCount() {
	//threadsMapLock->Acquire();
	//if (activeThreadCount < threadCount) {
		waitThreadCount++;
	//}
	//threadsMapLock->Release();
}

void
Process::decreaseWaitThreadCount() {
	//threadsMapLock->Acquire();
	//if (activeThreadCount > 0) {
		waitThreadCount--;
	//}
	//threadsMapLock->Release();
}

int
Process::getSignalThreadCount() {
	return signalThreadCount;
}

void
Process::increaseSignalThreadCount() {
	//threadsMapLock->Acquire();
	//if (activeThreadCount < threadCount) {
		signalThreadCount++;
	//}
	//threadsMapLock->Release();
}

void
Process::decreaseSignalThreadCount() {
	//threadsMapLock->Acquire();
	//if (activeThreadCount > 0) {
		signalThreadCount--;
	//}
	//threadsMapLock->Release();
}
